<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      /**
       * ES6借鉴C++, JAVA,C#和Python语言，引入了for...of循环，作为遍历所有数据结构的统一方法。
       *
       * 一个数据结构只要部署了Symboi.iterator属性，就被视为具有Iterator接口，就可以使用for...of语法
       * 也就是说，数组、Set、Map、和类数组，后文的Generator对象，以及字符串都可以使用for...of语法
       */
      // 1. 遍历数组
      var arr = ["red", "green", "blue"];
      for (let v of arr) {
        console.log(v); // red green blue
      }
      var obj = {};
      obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);
      for (let v of obj) {
        console.log("v", v); // red green blue
      }
      // 上面代码中，空对象obj部署了数组arr的Symbol.iterator属性，结果obj的for...of循环，产生了与arr完全一样的结果。

      // javascript原生的for...in循环，只能获得对象的键名，不能直接获取键值，for...of可以直接获取键值

      // for...of遍历数组，只返回具有数字索引的属性，如果数组中有按索引无法找到的属性，无法被遍历出，但是for...in可以找出
      var arr = [3, 5, 7];
      arr.foo = "hello";
      for (let i in arr) {
        console.log(i); // "0", "1", "2", "foo" 但是此时数组的长度仍然是3
      }
      for (let i of arr) {
        console.log(i); //  "3", "5", "7"
      }
      console.log("arr", arr);
    </script>
    <script>
      // set和map结构也原生具有Iterator接口，可以直接使用for..。of循环
      var engines = new Set(["Gecko", "Trident", "webkit", "webkit"]);
      for (var e of engines) {
        console.log(e);
      }
      // Gecko
      // Trident
      // Webkit
      var es6 = new Map();
      es6.set("edition", 6);
      es6.set("committee", "TC39");
      es6.set("standard", "ECMA-262");
      console.log(es6);
      for (var [name, value] of es6) {
        console.log(name + ": " + value);
      }
      // edition: 6
      // committee: TC39
      // standard: ECMA-262

      // 上面代码演示了如何遍历 Set 结构和 Map 结构。值得注意的地方有两个，首先，遍历的顺序是按照各个成员被添加进数据结构的顺序。
      // 其次，Set 结构遍历时，返回的是一个值，而 Map 结构遍历时，返回的是一个数组，
      // 该数组的两个成员分别为当前 Map 成员的键名和键值。
    </script>
    <script>
      // 遍历对象
      /**
       * 对于普通的对象，for...of结构不能直接使用，会报错，必须部署了 Iterator 接口后才能使用。
       * 但是，这样情况下，for...in循环依然可以用来遍历键名。
       * 对于普通的对象，for...in循环可以遍历键名，for...of循环会报错。
       * 一种解决方法是，使用Object.keys方法将对象的键名生成一个数组，然后遍历这个数组。
       * 另一个方法是使用 Generator 函数将对象重新包装一下。
       */
      var obj = { a: 1, b: 2, c: 3 };
      function* entrise(obj) {
        for (let key of Object.keys(obj)) {
          yield [key, obj[key]];
        }
      }
      for (let [key, value] of entrise(obj)) {
        console.log(key, "->", value);
      }
      // a -> 1
      // b -> 2
      // c -> 3
    </script>
  </body>
</html>
